package org.moonlight.jvm.rtda.share.heap.classmember;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.moonlight.jvm.classfile.MemberInfo;
import org.moonlight.jvm.rtda.share.heap.Class;

import java.io.Serializable;

/**
 * 类的成员信息 - 字段和方法都属于类的成员，它们有一些相同的信息（访问标志、名字、描述符）。
 *
 *
 * @author Moonlight
 * @createTime 2023/8/29 16:30
 **/
@Getter
@EqualsAndHashCode
public class ClassMember implements Serializable {

    /** 访问标识符 **/
    protected int accessFlags;
    /** 名字 **/
    protected String name;
    /** 描述符 **/
    protected String descriptor;
    /** 所属类 **/
    protected transient Class clazz;

    protected void copyMemberInfo(MemberInfo memberInfo) {
        this.accessFlags = memberInfo.getAccessFlags();
        this.name = memberInfo.getName();
        this.descriptor = memberInfo.getDescriptor();
    }

    /**
     * 类成员是否可以被 参数传入的类对象 访问
     * 1. 如果类成员是public，则任何类都可以访问。
     * 2. 如果类成员是protected，则只有子类和同一个包下的类可以访问。
     * 3. 如果类成员是默认访问权限（非public，非protected，也非privated），则只有同一个包下的类可以访问。
     * 4. 如果类成员是private，只有声明这个字段的类才能访问。
     *
     * @param d {@link Class} 类对象
     * @return boolean true 字段可以被 参数类对象 访问，反之不可以
     * @createTime 11:43 2023/8/30
     * @author moonlight
     **/
    public boolean isAccessibleTo(Class d) {
        if (this.isPublic()) {
            return true;
        }
        Class c = this.clazz;
        if (this.isProtected()) {
            return d.equals(c) || d.isSubClassOf(c) || c.getPackageName().equals(d.getPackageName());
        }
        if (!this.isPrivate()) {
            return c.getPackageName().equals(d.getPackageName());
        }
        return d.equals(c);
    }

    public boolean isPublic() {
        return (this.accessFlags & AccessFlags.ACC_PUBLIC) != 0;
    }

    public boolean isPrivate() {
        return (this.accessFlags & AccessFlags.ACC_PRIVATE) != 0;
    }

    public boolean isProtected() {
        return (this.accessFlags & AccessFlags.ACC_PROTECTED) != 0;
    }

    public boolean isFinal() {
        return (this.accessFlags & AccessFlags.ACC_FINAL) != 0;
    }

    public boolean isStatic() {
        return (this.accessFlags & AccessFlags.ACC_STATIC) != 0;
    }

    public boolean isSynthetic() {
        return (this.accessFlags & AccessFlags.ACC_SYNTHETIC) != 0;
    }

}
